﻿using System.Configuration;
using System;
using System.IO;
using System.Linq;
using System.Reflection;

namespace N2
{
	using Configuration;
	using Engine;
	using Persistence.NH;
	
	/// <summary>
	/// N2 Environment boot-strapper
	/// </summary>
	public class N2Launcher: IDisposable
	{
		#region Fields

		public readonly SessionSettings Settings;

		#endregion Fields

		#region ctor

		public N2Launcher(SessionSettings settings)
		{
			this.Settings = settings;
		}

		#endregion ctor

		#region Methods

		public void Initialize()
		{
			//TODO Learn more about assembly binding
			//HACK suggested at http://social.microsoft.com/Forums/en-US/netfxbcl/thread/e7cd62f8-f2d7-463c-9c43-8aa29720979c
			//Solution taken from http://nant.cvs.sourceforge.net/viewvc/*checkout*/nant/nant/src/NAnt.Core/Util/AssemblyResolver.cs?revision=1.8
			new NAnt.Core.Util.AssemblyResolver().Attach();

			//see comments to [ http://msdn.microsoft.com/en-us/library/ms224437(VS.85).aspx ]
			var _config = ConfigurationManager.OpenMappedExeConfiguration(
				new ExeConfigurationFileMap {
					ExeConfigFilename = this.Settings.ConfigurationFile,
				},
				ConfigurationUserLevel.None);
			
			//connection_string_name cannot be passed to NHibernate,
			// as it tries to resolve it from a current app's .config.
			// As a work-around we resolve it manually and pass directly as
			// a connection_string parameter.
			var _dbSection = (DatabaseSection)_config.GetSection("n2/database");
			var _cnName = _dbSection.ConnectionStringName;

			if (!string.IsNullOrEmpty(_cnName)) {
				var _cn = _config.ConnectionStrings.ConnectionStrings[_cnName].ConnectionString;

				_cn = _cn.Replace(
					"|DataDirectory|",
					Path.Combine(this.Settings.AppRoot, "App_Data"));

				_dbSection.HibernateProperties.Add(
					new NameValueConfigurationElement(
						NHibernate.Cfg.Environment.ConnectionString,
						_cn));
			}
			
			var _userAssemblies = this.Settings.AssemblyNameList
				.Select<AssemblyName, Assembly>(Assembly.Load)
				.ToList();
			
			var _engine = new Engine.ContentEngine(_config, "n2", EventBroker.Instance);
			var _typeFinder = _engine.Resolve<ITypeFinder>() as AppDomainTypeFinder;
			
			///Doing the following will block N2.dll too
			//if (null != _typeFinder) {
			//    _typeFinder.LoadAppDomainAssemblies = false;
			//}

			_typeFinder.AssemblySkipLoadingPattern += "|^LINQPad";
			
			//Necessary to bootstrap plugins..
			_engine.Initialize();
			
			Context.Initialize(_engine);

			var _cfgBuilder = Context.Current.Resolve<IConfigurationBuilder>() as N2.Persistence.NH.ConfigurationBuilder;
			_userAssemblies.ForEach(_cfgBuilder.Assemblies.Add);
		}

		#endregion Methods

		#region IDisposable Members

		//TODO
		void IDisposable.Dispose()
		{
			//throw new NotImplementedException();
		}

		#endregion
	}
}
